import os
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from time import sleep
from settings import *
from docx import Document
from docx.shared import Inches
import requests
import re


class ExamTreasureCrack:
    def __init__(self):
        # 创建目标文件夹
        if not os.path.exists(target_dir):
            os.makedirs(target_dir)
        # 创建图片文件夹
        self.img_dir_path = f'{target_dir}{os.sep}img'
        if not os.path.exists(self.img_dir_path):
            os.makedirs(self.img_dir_path)
        # 问题序号，初始为1
        self.question_index = 1
        # 题目数组
        self.choice_questions = []
        self.essay_questions = []
        # 实例化一个浏览器对象(传入浏览器的驱动程序)
        self.bro = self.create_driver()
        try:
            self.login()
            self.doc = Document()
            # 切换考试页面
            self.switch_exam_page()
            # 开始解析
            self.start_resolve_questions()
        except Exception as e:
            print(f"题目解析失败，原因可能是: {e}")
        finally:
            self.exit()

    def create_driver(self):
        if show:
            return webdriver.Chrome(executable_path=f'lib{os.sep}chromedriver.exe')
        # 实现无可视化界面操作
        options = Options()
        options.add_argument('--headless')
        options.add_argument('--disable-gpu')
        options.add_argument('--no-sandbox')
        options.add_argument('--disable-dev-shm-usage')
        # 应用到对象的选项中
        return webdriver.Chrome(executable_path=f'lib{os.sep}chromedriver.exe', options=options)

    def login(self):
        """
        登录考试宝
        """
        # 让浏览器对指定的url发送请求
        self.bro.get('https://www.kaoshibao.com/login/')
        # 找到账号和密码表单并输入账号密码
        username = self.bro.find_element_by_xpath(
            r'//*[@id="body"]/div[2]/div[2]/div[2]/form/div/div[1]/div/div/div/input')
        password = self.bro.find_element_by_xpath(
            r'//*[@id="body"]/div[2]/div[2]/div[2]/form/div/div[2]/div/div/div/input')
        username.send_keys(account['username'])
        password.send_keys(account['password'])
        sleep(0.5)
        # 点击登录按钮
        self.bro.find_element_by_xpath(r'//*[@id="body"]/div[2]/div[2]/div[2]/form/div/div[3]/button').click()
        sleep(1)
        print('登录成功...')

    def switch_exam_page(self):
        """
        切换到考试页面
        """
        self.bro.get(f'https://www.kaoshibao.com/online/?paperId={paper_id}')
        sleep(1)
        print('进入考试页面...')
        # 开启背题模式，这样可以直接显示答案
        recite_btn = self.bro.find_element_by_xpath(
            '//*[@id="body"]/div[2]/div[1]/div[2]/div[2]/div[2]/div[1]/p[2]/span[2]/div/span')
        self.bro.execute_script("arguments[0].click();", recite_btn)
        print('开启背题模式...')

    def start_resolve_questions(self):
        """
        开始解析题目
        """
        if radio_range_index:
            self.get_questions_by_range('单选题', radio_range_index, self.get_choice_question, self.choice_questions)
            print('单选题解析完成，请务关闭程序，否则进度可能丢失...')
        if checkbox_range_index:
            self.get_questions_by_range('多选题', checkbox_range_index, self.get_choice_question, self.choice_questions)
            print('多选题解析完成，请务关闭程序，否则进度可能丢失...')
        if judge_range_index:
            self.get_questions_by_range('判断题', judge_range_index, self.get_choice_question, self.choice_questions)
            print('判断题解析完成，请务关闭程序，否则进度可能丢失...')
        if fill_range_index:
            self.get_questions_by_range('填空题', fill_range_index, self.get_essay_question, self.essay_questions)
            print('填空题解析完成，请务关闭程序，否则进度可能丢失...')
        if essay_range_index:
            self.get_questions_by_range('问答题', essay_range_index, self.get_essay_question, self.essay_questions)
            print('问答题解析完成，请务关闭程序，否则进度可能丢失...')

    def get_questions_by_range(self, title, range_index, get_method, questions):
        """
        根据提供的标题和题号序列获取选择题题目(单选、多选、判断)内容
        """
        # 这个xpath是考试宝考试页面中的题号
        question_number_xpath = '//*[@id="body"]/div[2]/div[1]/div[2]/div[2]/div[1]/div[1]/span[{}]'
        for i in range(range_index[0], range_index[1] + 1):
            # 单击题号，显示该题目的内容
            self.bro.find_element_by_xpath(question_number_xpath.format(i)).click()
            sleep(0.5)
            # 将解析的题目放入题目列表
            questions.append(get_method())
            print(f'{title}{i}解析完成...')
        # 写入题目
        self.write_questions(title, questions)

    def get_choice_question(self):
        """
        获取选择题(包括单选、多选、判断)
        """
        choice_dict = {
            'question_text': '',
            'question_img': '',
            'options': '',
            'answer': ''
        }
        # 获取标题和标题图片
        question_box = self.bro.find_element_by_xpath(
            r'//*[@id="body"]/div[2]/div[1]/div[2]/div[1]/div/div[1]/div/div[1]/div/div')
        choice_dict['question_text'] = question_box.text
        if replace_space:
            choice_dict['question_text'] = choice_dict['question_text'].replace(' ', '')
        try:
            choice_dict['question_img'] = question_box.find_element_by_tag_name('img').get_attribute('src')
        except:
            pass
        # 获取选项
        options = self.bro.find_element_by_xpath(
            '//*[@id="body"]/div[2]/div[1]/div[2]/div[1]/div/div[1]/div/div[2]/div')
        options_text = options.text
        if replace_space:
            options_text = options_text.replace(' ', '')
        choice_dict['options'] = re.sub(r'(A|\n[B-Z])\n', r'\1.', options_text)
        # 获取答案
        answer = self.bro.find_element_by_xpath(
            '//*[@id="body"]/div[2]/div[1]/div[2]/div[1]/div/div[3]/div[1]/div/div[1]/div/b/span')
        choice_dict['answer'] = answer.text
        if replace_space:
            choice_dict['answer'] = choice_dict['answer'].replace(' ', '')
        # 返回解析好的选择题
        return choice_dict

    def get_essay_question(self):
        """
            获取问答题(包括填空、问答)
        """
        essay_dict = {
            'question_text': '',
            'question_img': '',
            'answer': '',
            'answer_img': ''
        }
        # 获取标题和标题图片
        question_box = self.bro.find_element_by_xpath(
            r'//*[@id="body"]/div[2]/div[1]/div[2]/div[1]/div/div[1]/div/div[1]/div/div')
        essay_dict['question_text'] = question_box.text
        if replace_space:
            essay_dict['question_text'] = essay_dict['question_text'].replace(' ', '')
        try:
            essay_dict['question_img'] = question_box.find_element_by_tag_name('img').get_attribute('src')
        except:
            pass
        # 获取答案
        answer_box = self.bro.find_element_by_xpath(
            '//*[@id="body"]/div[2]/div[1]/div[2]/div[1]/div/div[3]/div[1]/div/div[1]/p/span')
        essay_dict['answer'] = answer_box.text
        if replace_space:
            essay_dict['answer'] = essay_dict['answer'].replace(' ', '')
        # 去除答案的换行符，防止考试宝无法识别
        essay_dict['answer'] = essay_dict['answer'].replace('\n', ';\t')
        try:
            # 获取答案图片
            essay_dict['answer_img'] = answer_box.find_element_by_tag_name('img').get_attribute('src')
        except:
            pass
        # 返回解析好的问答题
        return essay_dict

    def write_questions(self, title, questions):
        """
        写入选择题
        """
        self.doc.add_heading(title, 1)
        for question in questions:
            self.doc.add_paragraph(f"{self.question_index}、{question['question_text']}")
            self.question_index += 1
            # 如果有图片则写入图片
            if question.get('question_img'):
                filename = self.download_img(question.get('question_img'))
                self.doc.add_picture(filename, width=Inches(6.25))
            # 如果有选项则输入选项(问答题无选项)
            if question.get('options'):
                self.doc.add_paragraph(question.get('options'))
            # 添加答案，如果有图片，则答案和图片在同一行，方便考试宝解析
            if question.get('answer_img'):
                filename = self.download_img(question.get('answer_img'))
                p = self.doc.add_paragraph()
                r = p.add_run(f"答案：{question.get('answer')}")
                r.add_picture(filename, width=Inches(6.25))
            else:
                self.doc.add_paragraph(f"答案：{question.get('answer')}")
            # 添加空行
            self.doc.add_paragraph()
        # 清空列表，以便后面不会再写入重复的数值
        questions.clear()

    def download_img(self, src):
        """
        下载图片并返回完整文件名(文件名包括路径)
        """
        response = requests.get(src)
        filename = f"{self.img_dir_path}{os.sep}{src.split('/')[-1]}"
        with open(filename, 'wb') as f:
            f.write(response.content)
        return filename

    def exit(self):
        """
        关闭程序
        """
        self.bro.quit()
        address = target_dir + os.sep + filename
        self.doc.save(address)
        if os.path.isabs(address):
            print(f'题目解析完成，文件地址：{address}')
        else:
            print(f'题目解析完成，文件地址：{os.getcwd() + os.sep + address}')


if __name__ == '__main__':
    os.environ['NO_PROXY'] = 'up.zaixiankaoshi.com'
    exam = ExamTreasureCrack()
